/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tencent is pleased to support the open source community by making behaviac available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at http://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections;
using System.Collections.Generic;

namespace behaviac
{
    public abstract class CompositeStochastic : BehaviorNode
    {
        public CompositeStochastic()
        {
		}

        ~CompositeStochastic()
        {
            m_method = null;
        }

        protected override void load(int version, string agentType, List<property_t> properties)
        {
            base.load(version, agentType, properties);

            foreach (property_t p in properties)
            {
                if (p.name == "RandomGenerator")
                {
                    if (p.value[0] != '\0')
                    {
                        this.m_method = Action.LoadMethod(p.value);
                    }//if (p.value[0] != '\0')
                }
                else
                {
                    //Debug.Check(0, "unrecognised property %s", p.name);
                }
            }
        }

        public override bool IsValid(Agent pAgent, BehaviorTask pTask)
        {
            if (!(pTask.GetNode() is CompositeStochastic))
            {
                return false;
            }

            return base.IsValid(pAgent, pTask);
        }

        protected CMethodBase m_method;

        public class CompositeStochasticTask : CompositeTask
        {
            public CompositeStochasticTask()
            {
			}

            ~CompositeStochasticTask()
            {
            }

			//generate a random float value between 0 and 1.
            public static float GetRandomValue(CMethodBase method, Agent pAgent)
            {
				float value = 0;
				if (method != null)
                {
					value = (float)method.Invoke(pAgent);
                }
                else
                {
					value = RandomGenerator.GetInstance().GetRandom();
                }

				Debug.Check(value >= 0.0f && value < 1.0f);
				return value;
			}

			public override void copyto(BehaviorTask target)
            {
                base.copyto(target);

                Debug.Check(target is CompositeStochasticTask);
                CompositeStochasticTask ttask = (CompositeStochasticTask)target;

                ttask.m_set = this.m_set;
            }

            public override void save(ISerializableNode node)
            {
                base.save(node);

                CSerializationID setId = new CSerializationID("set");
                node.setAttr(setId, this.m_set);
            }

            public override void load(ISerializableNode node)
            {
                base.load(node);
            }

            protected override bool onenter(Agent pAgent)
            {
                Debug.Check(this.m_children.Count > 0);

                this.random_child(pAgent);

                this.m_activeChildIndex = 0;
                return true;
            }

            protected override void onexit(Agent pAgent, EBTStatus s)
            {
            }

//            protected override EBTStatus update(Agent pAgent, EBTStatus childStatus)
//            {
//                bool bFirst = true;
//
//                Debug.Check(this.m_activeChildIndex != CompositeTask.InvalidChildIndex);
//
//                // Keep going until a child behavior says its running.
//                for (; ; )
//                {
//                    EBTStatus s = childStatus;
//                    if (!bFirst || s == EBTStatus.BT_RUNNING)
//                    {
//                        int childIndex = this.m_set[this.m_activeChildIndex];
//                        BehaviorTask pBehavior = this.m_children[childIndex];
//                        s = pBehavior.exec(pAgent);
//                    }
//
//                    bFirst = false;
//                    // If the child succeeds, or keeps running, do the same.
//                    if (s != EBTStatus.BT_FAILURE)
//                    {
//                        return s;
//                    }
//
//                    // Hit the end of the array, job done!
//                    ++this.m_activeChildIndex;
//                    if (this.m_activeChildIndex >= this.m_children.Count)
//                    {
//                        return EBTStatus.BT_FAILURE;
//                    }
//                }
//            }

            private void random_child(Agent pAgent)
            {
                Debug.Check(this.GetNode() == null || this.GetNode() is CompositeStochastic);
                CompositeStochastic pNode = (CompositeStochastic)(this.GetNode());

				int n = this.m_children.Count;
                if (this.m_set.Count != n)
                {
					this.m_set.Clear();

					for (int i = 0; i < n; ++i)
					{
						this.m_set.Add(i);
					}
                }

                for (int i = 0; i < n; ++i)
                {
					int index1 = (int)(n * GetRandomValue(pNode != null ? pNode.m_method : null, pAgent));
                    Debug.Check(index1 < n);

					int index2 = (int)(n * GetRandomValue(pNode != null ? pNode.m_method : null, pAgent));
                    Debug.Check(index2 < n);

                    //swap
                    if (index1 != index2)
                    {
                        int old = this.m_set[index1];
                        this.m_set[index1] = this.m_set[index2];
                        this.m_set[index2] = old;
                    }
                }
            }

            protected List<int> m_set = new List<int>();
        }
    }
}
